//searcher.hpp实现我们的系统服务器启动并且执行索引任务

#pragma once

#include "index.hpp"
#include "util.hpp"
#include "log.hpp"
#include <algorithm>
#include <json/json.h>

namespace my_searcher{
    //这是我们的服务器Searcher
    class Searcher
    {
    private:
        //初始化供我们系统查找的索引
        my_Index::Index *index;

    public:
        Searcher(){};
        ~Searcher(){};

    public:
        //初始化我们的Searcher服务器
        void InitSearcher(const std::string& input)
        {
            //1.我们需要获取并且创建我们的index单例对象
            index=my_Index::Index::GetInstance();
            //std::cout<<"获取index单例成功..."<<std::endl;
            LOG(NORMAL, "获取index单例成功...");

            //2.根据我们的index对象建立索引
            index->BuildIndex(input);
            //std::cout<<"建立正排索引和倒排索引成功..."<<std::endl;
            LOG(NORMAL, "建立正排和倒排索引成功...");
        }

        //用户使用我们搜索引擎的实现
        //query是用户输入的信息，json_string是我们要返回的浏览器搜索结果
        void Search(const std::string& query,std::string *json_string)
        {
            //1.我们要先对query进行分词
            //并将分好的词存入我们的words中
            std::vector<std::string> words; //用户输入的内容的分词
            my_util::JiebaUtil::CutString(query,&words);

            //2.将分好的词交由我们建立的index索引查找
            //创建一个存放倒排拉链的词库，用于我们之后存入对应word的倒排拉链
            my_Index::InvertedList inverted_list_all; 
            for(std::string word:words)
            {
                boost::to_lower(word); //搜索时要注意忽略大小写

                //从我们生成的单例索引index中找到word单词对应的倒排拉链
                my_Index::InvertedList *inverted_list=index->GetInvertedList(word); //查找倒排索引
                if(nullptr==inverted_list)
                {
                    continue; //word没有搜索结果，也要继续
                }

                //将word对应的倒排拉链插入到inverted_list_all的最后
                inverted_list_all.insert(inverted_list_all.end(),inverted_list->begin(),inverted_list->end());
            }

            //3.汇总查找的结果，按照相关性进行降序排序
            //使用lambda表达式对汇总的结果进行排序
            std::sort(inverted_list_all.begin(),inverted_list_all.end(),\
                    [](const my_Index::InvertedElem_t &e1,const my_Index::InvertedElem_t &e2){
                        return e1.weight>e2.weight;
                    });

            //4.根据查找的结果，构建我们要返回的json_string
            //通过我们的jsoncpp进行序列化和反序列化
            Json::Value root;
            for(auto &item:inverted_list_all)
            {
                my_Index::DocInfo_t *doc=index->GetForwardIndex(item.doc_id); //查找正排索引
                if(nullptr==doc)
                {
                    continue; //item没有搜索结果也要继续
                }

                Json::Value elem;
                elem["title"]=doc->title;
                elem["desc"]=GetDesc(doc->content,item.word); //content应该是文件的一部分，获取摘要
                elem["url"]=doc->url;
                elem["id"]=item.doc_id; //打印文档id
                elem["weight"]=item.weight; //打印word在文档的权值

                //将调整好的elem元素存放入root即将进行序列化
                root.append(elem);
            }

            //将root进行序列化并且返回给json_string
            Json::StyledWriter writer;
            *json_string=writer.write(root);
        }

         //GetDesc是我们获取文章摘要的函数
        std::string GetDesc(const std::string &html_content,const std::string &word)
        {
            //我们找到word在html_content中出现的首次位置,从该次前50个字符开始，到后100个字符就是我们的文章摘要
            //截取出这部分内容
            const int prev_step=50;
            const int next_step=100;

            //1.找到word首次出现的地方
            //为了找到html_content中和word匹配的字段，而且要忽略html_content中的大小写，且不能改变源文件
            auto iter=std::search(html_content.begin(),html_content.end(),word.begin(),word.end(),[](int x,int y){
                return (std::tolower(x)==std::tolower(y));
            });
            //int pos=html_content.find(word); 这个无法忽略大小写
            if(iter==html_content.end())
            {
                return "None";
            }
            //获得迭代器到文档开始的距离
            int pos=std::distance(html_content.begin(),iter);

            //2.获取start和end
            int start=0;
            int end=html_content.size()-1;
            //如果之前有50多个字符就更新位置
            if(pos>start + prev_step) start = pos - prev_step;
            if(pos<end - next_step) end = pos + next_step;

            //3.截取子串并且返回
            if(start>=end) return "None";
            std::string desc=html_content.substr(start,end-start);
            desc+="...";
            return desc;
        }
    };
}